using System;
using System.Collections.Generic;
using System.Text;

namespace EventBroker
{
	/// <summary>
	/// Represents a point of communication on a certain m_strTopic between the m_strTopic publishers and the m_strTopic subscribers.
	/// </summary>
	public class CEventTopic : IDisposable
	{
		private string m_strName;
		private bool m_bIsEnabled = true;
		private List<CPublication> m_aPublications = new List<CPublication>();
        private List<CSubscription> m_aSubscriptions = new List<CSubscription>();
		private object m_objLock = new object();  
        
			
        public CEventTopic(string strTopic_p)
        {
            m_strName = strTopic_p;
        }

        	
		/// <summary>
		/// Gets the m_strTopic m_strName.
		/// </summary>
		public string Name
		{
			get { return m_strName; }
		}


		/// <summary>
		/// Gets or sets the m_bIsEnabled objState_p of the m_strTopic. A disable m_strTopic will not fire events.
		/// </summary>
		public bool Enabled
		{
			get { return m_bIsEnabled; }
			set { m_bIsEnabled = value; }
		}


		/// <summary>
		/// Adds a publication to the m_strTopic.
		/// </summary>
		public virtual void AddPublication(object objPublisher_p, string strEventName_p)
		{
			CPublication publication = new CPublication(this, objPublisher_p, strEventName_p);
			m_aPublications.Add(publication);			
		}


        /// <summary>
        /// Adds a subcription to this eventTopic_p
        /// </summary>
        public virtual void AddSubscription(object objSubscriber_p,
                                            string strHandlerMethodName_p,
                                            Type[] aParameterTypes_p,
                                            EThreadOption eThreadOption_p,
                                            int nSelectedWorkItemGroupID_p)
        {
            lock (m_objLock)
            {
                if (FindSubscription(objSubscriber_p, strHandlerMethodName_p) == null)
                {
                    m_aSubscriptions.Add(new CSubscription(objSubscriber_p, 
                                                           strHandlerMethodName_p,
                                                           aParameterTypes_p,
                                                           eThreadOption_p,
                                                           nSelectedWorkItemGroupID_p));
                }
            }
        }

        			
		/// <summary>
		/// Fires the eventTopic_p
		/// </summary>
		public virtual void Fire(object objSender_p, EventArgs eventArgs_p)
		{
			if (m_bIsEnabled)
			{
				CallSubscriptionHandlers(objSender_p, eventArgs_p, GetHandlers());				
			}
		}


        private dgEventTopicFireDelegate[] GetHandlers()
        {
            lock (m_objLock)
            {
                List<dgEventTopicFireDelegate> aResults = new List<dgEventTopicFireDelegate>();
                        
                foreach (CSubscription subscription in m_aSubscriptions.ToArray())
                {
                    dgEventTopicFireDelegate dgEventTopicFire = subscription.GetHandler();
                    if (dgEventTopicFire != null)
                    {
                        aResults.Add(dgEventTopicFire);
                    }
                }
                return aResults.ToArray();
            }
        }


		private void CallSubscriptionHandlers(object objSender_p, 
                                              EventArgs eventArgs_p, 
                                              dgEventTopicFireDelegate[] aEventTopicFireDelegates_p)
		{
			foreach (dgEventTopicFireDelegate dgEventTopicFire in aEventTopicFireDelegates_p)
			{
				dgEventTopicFire(objSender_p, eventArgs_p);
			}
		}

        
		private CPublication FindPublication(object objPublisher_p, string strEventName_p)
		{
			CPublication publication = m_aPublications.Find(delegate(CPublication publication_p)
			{
				return publication_p.Publisher == objPublisher_p && publication_p.EventName == strEventName_p;
			});
			return publication;
		}


        private CSubscription FindSubscription(object objSubscriber_p, string strHandlerMethodName_p)
        {
            lock (m_objLock)
            {
                CSubscription subscription = m_aSubscriptions.Find(delegate(CSubscription subscription_p)
                {
                    return ((subscription_p.Subscriber == objSubscriber_p) && (subscription_p.HandlerMethodName == strHandlerMethodName_p));
                });
                return subscription;
            }
        }


        /// <summary>
        /// Removes a publication from the m_strTopic.
        /// </summary>
        public virtual void RemovePublication(object objPublisher_p, string strEventName_p)
        {
            CPublication publication = FindPublication(objPublisher_p, strEventName_p);
            if (publication != null)
            {
                m_aPublications.Remove(publication);
                publication.Dispose();
            }
        }


        public void RemoveSubscription(object objSubscriber_p, string strHandlerMethodName_p)
        {
            lock (m_objLock)
            {
                CSubscription subscription = FindSubscription(objSubscriber_p, strHandlerMethodName_p);
                if (subscription != null)
                {
                    RemoveSubscription(subscription);
                }
            }
        }


        private void RemoveSubscription(CSubscription subscription_p)
        {
            lock (m_objLock)
            {
                m_aSubscriptions.Remove(subscription_p);
            }
        }


        /// <summary>
        /// Perform a sanity cleaning of the dead references to publishers and subscribers
        /// </summary>
        private void Clean()
        {
            lock (m_objLock)
            {
                foreach (CSubscription subscription in m_aSubscriptions.ToArray())
                {
                    if (subscription.Subscriber == null)
                    {
                        RemoveSubscription(subscription);
                    }
                }
            }
            foreach (CPublication publication in m_aPublications.ToArray())
            {
                if (publication.Publisher == null)
                {
                    m_aPublications.Remove(publication);
                    publication.Dispose();
                }
            }
        }


        /// <summary>
        /// Called to free resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }


        /// <summary>
        /// Called to free resources.
        /// </summary>
        protected virtual void Dispose(bool bIsDisposing_p)
        {
            if (bIsDisposing_p)
            {
                foreach (CPublication publication in m_aPublications)
                {
                    publication.Dispose();
                }
                m_aPublications.Clear();


                lock (m_objLock)
                {
                    foreach (CSubscription subscription in m_aSubscriptions.ToArray())
                    {
                        if (subscription.Subscriber == null)
                        {
                            RemoveSubscription(subscription);
                        }
                    }
                }
            }
        }
	}
}
